package suncertify.application;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class defines all string resources that appear in the user interface, like buttons, menus and tool tips. This
 * class can be modified in the future to provide multiple language support for the application, in this version though
 * all string values are hard coded in English. The class main goal is to hold all strings that are relevant to
 * localization to make the multiple language support in the future centralized and easier.
 * 
 * <p>
 * The string constants are named as follows:
 * <ul>
 * <li>Constants that start with <b>CAP</b> (means caption) refer to strings that appear in the user interface controls,
 * e.g. <b>CAP_Cancel</b> is used as the text for the cancel button.</li>
 * <li>Constants that start with <b>TIP</b> refer to strings that appear in tool tips.</li>
 * <li>Constants that start with <b>MSG</b> refer to strings that appear as messages in the application e.g. error
 * messages or exception messages.</li>
 * </ul>
 * 
 * <p>
 * The string values are of two types:
 * <ol>
 * <li>Parameter less string values e.g. {@link #CAP_CANCEL} contains the string "Cancel". These strings can be used
 * directly using <code>Strings.CAP_CANCEL</code></li>
 * <li>Strings with standard java string format specifiers (e.g. %1$s). These strings must be retrieved with the
 * {@link #getMessage(String, String...)} method. These kind of string resources are allowed to have only string format
 * specifiers like %1$s.</li>
 * </ol>
 * 
 * <p>
 * Note: The string resources in the <code>throw new IllegalArgumentException()</code> and the string resources in the
 * {@link Log} messages are not included in <code>Strings</code> class.
 * 
 */
public class Strings {

    /**
     * String resource for the application's frame title bar text in the client
     * {@link ApplicationMode#STANDALONE_CLIENT} mode.
     */
    public static final String CAP_CLIENT_ALONE_TITLE = "Bodgitt and Scarper Booking Client [Stand alone mode]";

    /**
     * String resource for the application's frame title bar text in the {@link ApplicationMode#NETWORK_CLIENT} mode.
     */
    public static final String CAP_CLIENT_NETWORK_TITLE = "Bodgitt and Scarper Booking Client [Network mode]";

    /**
     * String resource for the application's frame title bar text in the {@link ApplicationMode#SERVER} mode.
     */
    public static final String CAP_SERVER_TITLE = "Bodgitt and Scarper Booking Server";

    /**
     * String resource definition.
     */
    public static final String CAP_INFORMATION = "Information";

    /**
     * String resource definition.
     */
    public static final String CAP_FILE = "File";

    /**
     * String resource definition.
     */
    public static final String CAP_QUIT = "Quit";

    /**
     * String resource definition.
     */
    public static final String CAP_BOOK = "Book...";

    /**
     * String resource definition.
     */
    public static final String CAP_UNBOOK = "Clear Booking";

    /**
     * String resource definition.
     */
    public static final String CAP_SEARCH_FOR_NAME = "Search for name:";

    /**
     * String resource definition.
     */
    public static final String CAP_LOCATION = "and city:";

    /**
     * String resource definition.
     */
    public static final String CAP_SEARCH = "Search";

    /**
     * String resource definition.
     */
    public static final String CAP_REFRESH = "Refresh";

    /**
     * String resource definition.
     */
    public static final String CAP_BOOK_CONTRACTOR = "Book Contractor";

    /**
     * String resource definition.
     */
    public static final String CAP_BOOK_TIMER_INFO = "This dialog will automatically close in %1$d seconds!";

    /**
     * String resource definition.
     */
    public static final String CAP_BOOK_TO_THIS_CUSTOMER = "Book to this customer";

    /**
     * String resource definition.
     */
    public static final String CAP_REMOVE_BOOKING_FROM_CUSTOMER = "Remove booking from the contractor";

    /**
     * String resource definition.
     */
    public static final String CAP_CUSTOMER_ID = "Customer ID:";

    /**
     * String resource definition.
     */
    public static final String CAP_OK = "OK";

    /**
     * String resource definition.
     */
    public static final String CAP_CANCEL = "Cancel";

    /**
     * String resource definition.
     */
    public static final String CAP_OPEN_FILE = "Open File";

    /**
     * String resource definition.
     */
    public static final String CAP_CONNECT = "Connect";

    /**
     * String resource definition.
     */
    public static final String CAP_DATABASE_FILE = "Open Database File";

    /**
     * String resource definition.
     */
    public static final String CAP_REMOTE_SERVER = "Connect to Server";

    /**
     * String resource definition.
     */
    public static final String CAP_PORT = "Port number:";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_NAME = "Name";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_CITY = "City";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_SPECIALTIES = "Specialties";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_STAFF_NUMBER = "Staff Number";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_CHARGE = "Hourly Charge";

    /**
     * String resource definition.
     */
    public static final String CAP_CONTRACTOR_PROPERTY_OWNER = "Customer";

    /**
     * String resource definition.
     */
    public static final String CAP_START = "Start";

    /**
     * String resource definition.
     */
    public static final String CAP_STOP = "Stop";

    /**
     * String resource definition.
     */
    public static final String CAP_ERROR = "Error";

    /**
     * String resource definition.
     */
    public static final String CAP_TERMINATE = "Terminate";

    /**
     * String resource definition.
     */
    public static final String CAP_DATABASE_FILE_LABEL = "Database file:";

    /**
     * String resource definition.
     */
    public static final String CAP_BROWSE = "Browse...";

    /**
     * String resource definition.
     */
    public static final String CAP_DATABASE_FILE_EXTENSION = "db";

    /**
     * String resource definition.
     */
    public static final String DATABASE_FILE_CHOOSER_DESCRIPTION = "Database files (*." + CAP_DATABASE_FILE_EXTENSION
	    + ")";

    /**
     * String resource definition.
     */
    public static final String CAP_SERVER_NAME = "Server name:";

    /**
     * String resource definition.
     */
    public static final String CAP_CONFIG_FILE_HEADER = "suncertify configuration file";

    /**
     * String resource definition.
     */
    public static final String TIP_BOOK_CONTRACTOR = "Books the selected contractor in the list for a customer.";

    /**
     * String resource definition.
     */
    public static final String TIP_UNBOOK_CONTRACTOR = "Removes booking from the selected contractor.";

    /**
     * String resource definition.
     */
    public static final String TIP_BOOK_DIALOG_TIMER = "When this dialog is opened no other user can modify the selected contractor! this dialog is closed automatically to prevent one user from blocking other users forever.";

    /**
     * String resource definition.
     */
    public static final String TIP_SEARCH = "Searches for contractors that match the provided name and/or city. An empty field matches all values in that field.";

    /**
     * String resource definition.
     */
    public static final String TIP_CLEAR_SEARCH = "Clears the name and city fields and shows all contractors.";

    /**
     * String resource definition.
     */
    public static final String TIP_NAME_FIELD_SEARCH = "Enter the value you are looking for e.g. 'fred' matches 'fred', 'Fred' and 'Freddy'";

    /**
     * String resource definition.
     */
    public static final String TIP_CITY_FIELD_SEARCH = "Enter the value you are looking for e.g. 'who' matches 'whoville', 'WHOVILLE' and 'who'";

    /**
     * String resource definition.
     */
    public static final String TIP_BROWSE_BUTTON = "Opens the system's open file dialog.";

    /**
     * String resource definition.
     */
    public static final String TIP_DATABASE_FILE = "Type the path of the application's database file, or click the 'browse...' button to locate it.";

    /**
     * String resource definition.
     */
    public static final String TIP_PORT_SERVER = "Type the TCP port (between 1 and 65000) on which the server will receive client request.";

    /**
     * String resource definition.
     */
    public static final String TIP_PORT_CLIENT = "Type the TCP port (1-65000) of the server you wish to connect to.";

    /**
     * String resource definition.
     */
    public static final String TIP_SERVER_NAME = "Type the name of the server you wish to connect to.";

    /**
     * String resource definition.
     */
    public static final String TIP_SERVER_START = "Starts the server and allows clients to connect to it.";

    /**
     * String resource definition.
     */
    public static final String TIP_SERVER_STOP = "Stops the server and disconnects all connected clients.";

    /**
     * String resource definition.
     */
    public static final String TIP_SERVER_TERMINATE = "Stops and terminates the server application.";

    /**
     * String resource definition.
     */
    public static final String TIP_OWNER_TEXTFIELD = "Customer ID is an eight digit number between 10000000 and 99999999 that represents the customer for this contractor.";

    /**
     * String resource definition.
     */
    public static final String MSG_NO_CONTRACTOR_SELECTED = "Please select a contractor first!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_ALREADY_BOOKED = "The selected conractor is already booked to a customer!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_READ_ERROR = "Failed to retrieve all contractors from the database!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_FIND_ERROR = "Failed to search the contractors!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_ALREADY_DELETED = "The selected contractor is already deleted by another user. Please refresh you contractors list to get an up to date list of contractors.";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_SECURITY_ERROR = "An attempt was made to update/delete a contractor with a wrong security token!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_INIT_ERROR = "The contractors database failed to initialize! Possible reasons: the database file could not be accessed or the database file was accessed successfully but contains corrupt data!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_REMOTE_INIT_ERROR = "Failed to connect to the server application! Make sure: 1) The server and port number are correct. 2) The network connection to the server is working. 3) The server application is running and started. ";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_MAX_VALUE = "The maximum value for the UnsignedIntegerJTextFiled must be greater than 0 and less or equal Integer.MAX_VALUE!";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_CUSTOMER_ID = "Invalid customer ID! The customer ID is an eight digit positive integer between 10,000,000 and 99,999,999";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_FILE_NOT_FOUND = "Database file not found! Please specify a valid database file.";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_FILE_WRONG_TYPE = "Wrong database file extension! Valid database files have the extension '.db'";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_FILE_READ_ERROR = "Databse file cannot be read!";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_FILE_WRITE_ERROR = "Databse file cannot be written!";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_READ_ERROR = "An input/output error occured while reading from the database file!";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_WRONG_FORMAT_ERROR = "Wrong file format! The first four bytes in the database file are not: 00, 00, 02, 02! The content of the selected file do not represent a valid database!";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_OFFSET_ERROR = "Wrong file schema info! The offset to the first record in the database file must not be larger than the file length!";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_LENGTH_ERROR = "Invalid file content or wrong schema info! The total size of the file does not equal an integer multiple of one record size plus the file schema size!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONFIG_FILE_SAVE_ERROR = "Failed to save the database file path to the suncertify.properties file! This is will not affect the application from working correctly. Exception: %1$s";

    /**
     * String resource definition.
     */
    public static final String MSG_CONFIG_REMOTE_SAVE_ERROR = "Failed to save the server and/or port number to the suncertify.properties file! This is will not affect the application from working correctly. Exception: %1$s";

    /**
     * String resource definition.
     */
    public static final String MSG_CONFIG_FILE_PORT_SAVE_ERROR = "Failed to save the database path and/or port number to the suncertify.properties file! This will not affect the application from working correctly. Exception: %1$s";

    /**
     * String resource definition.
     */
    public static final String MSG_CONFIG_FILE_READ_ERROR = "A file read error happend while reading your settings from the application configuration file (suncertify.properties)! The application will start without any user settings.";

    /**
     * String resource definition.
     */
    public static final String MSG_CONFIG_FILE_READ_PERMISSION_ERROR = "The application has no permission to read its configuration file (suncertify.properties)! The application will start with default settings.";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_PROPERTY_NAME = "'%1$s' is not a valid user configuration property name!";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_CONTRACTOR_PROPERTY_INDEX = "index = %1$s is not a valid contractor property index!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_STRING = "Name=%1$s, City=%2$s, Specialties=%3$s, Staff count=%4$s, Charge=%5$s, Owner=%6$s";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_NAME = "Invalid name field! A valid name field must not be empty and must consist of 1 to 32 alphanumeric (0-9, a-z, A-Z), space, '&', ',' characters.";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_LOCATION = "Invalid city field! A valid city field must not be empty and must consist of 1 to 64 alphanumeric (0-9, a-z, A-Z), space, '&', ',' characters.";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_SPECIALTIES = "Invalid specialty field! A valid specialty field must not be empty and must consist of 1 to 64 alphanumeric (0-9, a-z, A-Z), space, '&', ',' characters.";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_SIZE = "Invalid size field! A valid size must be numeric between 1 and 999999.";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_RATE = "Invalid rate field! A valid rate must be a currency value between $1.00 and $9999.99";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_FIELD_OWNER = "Invalid customer field! A valid customer must be numeric between 10000000 and 99999999";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_FORMAT_ERROR = "The Contractors constructor failed because of a database file format error! Please make sure the database file has a valid format.";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTORS_ACCESS_ERROR = "The Contractors constructor failed because of a database file access error! Please make sure the database file can be accessed correctly.";

    /**
     * String resource definition.
     */
    public static final String MSG_FIND_CONTRACTOR_FAILED = "Find contracotr(s) by criteria failed!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_NOTFOUND = "No contractor with ID = %1$s exists!";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_UPDATE_ERROR = "Failed to update contractor %1$s.";

    /**
     * String resource definition.
     */
    public static final String MSG_CONTRACTOR_UPDATE_ERROR2 = "Failed to book the selected contractor! Most probable reason is the contractor has been booked by another user. Refresh the contractors to see the changes done by other users.";

    /**
     * String resource definition.
     */
    public static final String MSG_DATABASE_FILE_CLOSE_ERROR = "Failed to close the database file properly!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_INVALID_FIELD_COUNT = "Invalid record field count! a record must have %1$s fields.";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_NOT_UNIQUE_ERROR = "Record with (name=%1$s) and (city=%2$s) already exists in the database! The name and city values of each record must be unqiue!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_WRITE_IO_ERROR = "Failed to change the record (name=%1$s, city=%2$s) in the database file because of an IO error!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_WRITE_IO_ERROR2 = "Failed to change the record (ID=%1$s) in the database file because of an IO error!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_ACCESS_DELETED_RECORD = "An attempt was made to access a deleted record! (record ID=%1$s)";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_INVALID_ID = "Invalid record number %1$s! A valid record number is greater or equal 0 and less than record count.";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_READ_ALL_ERROR = "Failed to read all records from the database!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_FIND_CRITERIA_ERROR = "Search for records by criteria failed! Check the causing exception for more details.";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_CRITERIA_ERROR = "The record search criteria '%1$s' is invalid! A valid search criteria must not be null and must contain %2$s entries. Each entry corresponds to one record field.";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_MODFIY_UNLOCKED = "An attempt was made to modify record %1$s which was not locked before!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_WRONG_LOCK_COOKIE = "An attempt was made to modify record %1$s using a wrong cookie!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_WRONG_UNLOCK_COOKIE = "Wrong unlock cookie! An attempt was made to unlock record %1$s with a wrong cookie!";

    /**
     * String resource definition.
     */
    public static final String MSG_RECORD_UNLOCK_ERROR = "An attempt was made to unlock record %1$s which was not locked before!";

    /**
     * String resource definition.
     */
    public static final String MSG_SERVER_START_ERROR = "The RMI server failed to start! Possible reason(s): The port number is already used, the database file is corrupt or cannot be accessed.";

    /**
     * String resource definition.
     */
    public static final String MSG_SERVER_STOP_ERROR = "Failed to stop the RMI sever correctly!";

    /**
     * String resource definition.
     */
    public static final String MSG_SERVER_CLIENT_ERROR = "Failed to contact the server! Java RMI failed to contact the remote server!";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_SERVER_NAME = "Please provide a valid server name!";

    /**
     * String resource definition.
     */
    public static final String MSG_INVALID_PORT_NUMBER = "Please provide a valid port number! (1 - 65000)";

    /**
     * String resource definition.
     */
    public static final String MSG_REMOTE_CONNECTION_ERROR = "Failed to communicate with the server application! Make sure the server application is running and reachable over the network. Restart the application and connect again.";

    /**
     * String resource definition.
     */
    public static final String MSG_LOGGER_INITIALIZATION_ERROR = "Logger failed to initialize! The application will work normally, no log file will be created.";

    /**
     * String resource definition.
     */
    public static final String MSG_ACTION_LISTENER_GENERAL_EXCEPTION = "An unexpected error happened! Exception:%1$s";

    /**
     * String resource definition.
     */
    public static final String MSG_DATA_IllegalArgumentException = "The data argument in the DBAccess interface cannot be null!";

    /**
     * String resource definition.
     */
    public static final String MSG_NULL_EMPTY_PARAMETER = "Parameter %1$s is null or empty!";

    /**
     * String resource definition.
     */
    public static final String MSG_CLIENT_ALONE_DATABASE_ERROR = "The contractors records could not be read or parsed from the database file! Possible reason(s): The access to the file system failed or at least one of the contractor record fields has an invalid format e.g. a numeric field does not contain a numeric value. The application cannot start with this database file! click OK to terminate the application.";

    /**
     * String resource definition.
     */
    public static final String MSG_SERVER_NOT_RUNNING = "The server has been stopped!";

    /**
     * String resource definition.
     */
    public static final String MSG_SERVER_NOT_RUNNING_MESSAGE = "The server has been stopped while it was processing your request!";

    /**
     * Returns the string resource after it substitutes the format specifiers with the supplied parameters. If no
     * parameters are supplied then the <code>stringID</code> parameter is returned as it is. If the number of format
     * specifiers in the <code>stringID</code> is not equal to the number of format specifiers in the
     * <code>parameters</code> array then the <code>stringID</code> parameter is returned as it is.
     * 
     * @param stringID
     *            The string ID to be returned.
     * @param parameters
     *            The parameters with which the string resource will be formatted.
     * 
     * @return Formatted string resource.
     * 
     * @throws IllegalArgumentException
     *             Thrown if the <code>stringID</code> parameter is null or empty.
     * 
     * @see String#format(String, Object...)
     * 
     */
    public static String getMessage(String stringID, String... parameters) {

	// make sure the stringID is not null or empty
	if (stringID == null || stringID.isEmpty()) {
	    throw new IllegalArgumentException("stringID is null or empty!");
	}

	// if the parameters are not there then simply return the stringID
	if (parameters == null || parameters.length == 0) {
	    return stringID;
	}

	// make sure the number of formatting specifiers equals the number of supplied parameters.
	if (parameters.length != getFormatParameterCount(stringID)) {

	    assert false : "An attempt was made to format a message with wrong number of format parametres!";
	    return stringID;
	}

	// format and return the string resource.
	return String.format(stringID, (Object[]) parameters);
    }

    /**
     * Returns the number of format specifiers in <code>s</code>. Only string format specifiers are counted that look
     * like <code>%1$s, %2$s...%n$s</code>
     * 
     * @param s
     *            The string that contains 0 or more format specifiers.
     * @return The number of format specifiers.
     * 
     * @see String#format(String, Object...)
     */
    private static int getFormatParameterCount(String s) {

	// patter that matches %1$s, %2$s, %3$s...etc.
	Pattern pattern = Pattern.compile("\\%[0-9]+\\$s");
	Matcher matcher = pattern.matcher(s);

	// counter for the matches
	int count = 0;

	// find and count
	while (matcher.find()) {
	    ++count;
	}

	return count;
    }
}
